﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Altbrot.Yawr.Rss;
using Altbrot.Yawr.Pls;
using System.IO;
using System.Net;
using Altbrot.Yawr.Player;
using System.Threading;
using System.ComponentModel;
using System.Collections.Specialized;
using Altbrot.Yawr.Buslogic;
using Altbrot.Yawr.Entities;
using System.Windows.Forms;

namespace Altbrot.Yawr.ShoutCast
{
    /// <summary>
    /// GetStepCompleted Callback Handler
    /// </summary>
    public delegate void GetStepCompletedEventHandler(object sender, GetStepCompletedEventArgs e);

    public delegate void UpdateCompletedEventHandler(object sender,UpdateCompletedEventArgs e);

    public delegate void UpdateProgressChangedEventHandler(object sender, UpdateProgressEventArgs e);    

    /// <summary>
    /// ShoutCast Manager Class
    /// </summary>
    public class ShoutCastManager
    {
        #region member

        /// <summary>
        /// Current Manager State
        /// </summary>
        //private ShoutCastManagerState _currentState;

        /// <summary>
        /// Temporary download pls file
        /// </summary>
        private readonly string _tmpPlsFilePath;

        /// <summary>
        /// Stream player
        /// </summary>
        private IStreamPlayer _player;

        /// <summary>
        /// Current Station
        /// </summary>
        private FeedItem _currentGenreItem;

        /// <summary>
        /// User State Collection
        /// </summary>
        private HybridDictionary _userStateToLifetime = new HybridDictionary();

        /// <summary>
        /// WorkerEventHandler 
        /// </summary>
        private delegate void WorkerEventHandler(Uri baseUrl, string genre, FeedItemCategory currentState, AsyncOperation asyncOp);

        /// <summary>
        /// 
        /// </summary>
        private delegate void UpdateWorkerEventHandler(FeedItem feedItem, AsyncOperation asyncOp);

        /// <summary>
        /// SelectedFeedItem
        /// </summary>
        private FeedItem _selectedFeedItem;

        /// <summary>
        /// private events
        /// </summary>
        private SendOrPostCallback onBaseStepCompletedDelegate;
        private SendOrPostCallback onNextStepCompletedDelegate;
        private SendOrPostCallback onPrevStepCompletedDelegate;
        private SendOrPostCallback onUpdateCompletedDelegate;
        private SendOrPostCallback OnUpdateProgressDelegate;

        #endregion

        #region Public events

        public event GetStepCompletedEventHandler GetBaseStepCompleted;
        public event GetStepCompletedEventHandler GetNextStepCompleted;
        public event GetStepCompletedEventHandler GetPrevStepCompleted;
        public event UpdateCompletedEventHandler UpdateCompleted;
        public event UpdateProgressChangedEventHandler UpdateProgressChanged;
        public event PlayerStatusChangedEventHandler OnPlayerStatusTextChanged;
        public event MethodInvoker OnMediaChanged;

        #endregion

        #region props

        public string MediaName
        {
            get
            {
                if (_player != null)
                {
                    return _player.MediaName;
                }
                return string.Empty;
            }
        }

        public string MediaBitrate
        {
            get
            {
                if (_player != null)
                {
                    return _player.MediaBitrate;
                }
                return string.Empty;
            }
        }

        public StreamPlayerPlayState PlayerState
        {
            get
            {
                return _player.PlayerState;
            }
        }

        /// <summary>
        /// Gets or sets the shout cast base URL.
        /// </summary>
        /// <value>The shout cast base URL.</value>
        public Uri ShoutCastBaseUrl { get; set; }

        /// <summary>
        /// Gets or sets the selected feed item.
        /// </summary>
        /// <value>The selected feed item.</value>
        public FeedItem SelectedFeedItem 
        {
            get
            {
                return _selectedFeedItem;
            }
            set
            {
                _selectedFeedItem = value;
                if (_selectedFeedItem.Category == FeedItemCategory.Station.ToString())
                    this._currentGenreItem = _selectedFeedItem;
            }
        }

        /// <summary>
        /// Gets or sets the manager.
        /// </summary>
        /// <value>The manager.</value>
        public IBuslogicManager Manager { get; set; }

        #endregion

        #region ctor

        /// <summary>
        /// Initializes a new instance of the <see cref="ShoutCastManager"/> class.
        /// </summary>
        /// <param name="shoutCastBaseUrl">The shout cast base URL.</param>
        public ShoutCastManager(Uri shoutCastBaseUrl, string tmpPlayListFile, IBuslogicManager man)
        {
            this.ShoutCastBaseUrl = shoutCastBaseUrl;
            this._tmpPlsFilePath = tmpPlayListFile;
            this._player = new WMPLibPlayer();
            this._player.OnStatusTextChanged += new PlayerStatusChangedEventHandler(_player_OnStatusTextChanged);
            this._player.OnMediaChanged += new MethodInvoker(_player_OnMediaChanged);
            this.Manager = man;
            this.Manager.OnUpdateProgress += new UpdateProgressMethodDelegate(UpdateProgressMethod);

            InitializeDelegates();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ShoutCastManager"/> class.
        /// </summary>
        /// <param name="shoutCastBaseUrl">The shout cast base URL.</param>
        public ShoutCastManager(Uri shoutCastBaseUrl, string tmpPlayListFile, IBuslogicManager man, Form form)
        {
            this.ShoutCastBaseUrl = shoutCastBaseUrl;
            this._tmpPlsFilePath = tmpPlayListFile;
            this._player = new WMPLibPlayer(form);
            //this._player = new CFPlayer(form as centrafuse.Plugins.CFPlugin);
            this._player.OnStatusTextChanged += new PlayerStatusChangedEventHandler(_player_OnStatusTextChanged);
            this._player.OnMediaChanged += new MethodInvoker(_player_OnMediaChanged);
            this.Manager = man;
            this.Manager.OnUpdateProgress += new UpdateProgressMethodDelegate(UpdateProgressMethod);

            InitializeDelegates();
        } 

        #endregion

        #region public

        /// <summary>
        /// Initializes the delegates.
        /// </summary>
        protected virtual void InitializeDelegates()
        {
            onBaseStepCompletedDelegate = new SendOrPostCallback(BaseStepCompleted);
            onNextStepCompletedDelegate = new SendOrPostCallback(NextStepCompleted);
            onPrevStepCompletedDelegate = new SendOrPostCallback(PrevStepCompleted);
            onUpdateCompletedDelegate = new SendOrPostCallback(UpdateCompletedHandler);
            OnUpdateProgressDelegate = new SendOrPostCallback(UpdateProgressHandler);
        }

        /// <summary>
        /// Gets the state of the current.
        /// </summary>
        /// <returns></returns>
        public FeedItemCategory GetCurrentState()
        {
            if (this.SelectedFeedItem != null)
            {
                return (FeedItemCategory)Enum.Parse(typeof(FeedItemCategory), this.SelectedFeedItem.Category);
            }
            else
            {
                return FeedItemCategory.Undefined;
            }
        }

        /// <summary>
        /// Gets the current genre item.
        /// </summary>
        /// <returns></returns>
        public FeedItem GetCurrentGenreItem()
        {
            return this._currentGenreItem;
        }

        /// <summary>
        /// Plays the URL.
        /// </summary>
        public void PlayUrl()
        {
            if (this.SelectedFeedItem != null && this.SelectedFeedItem.Category == FeedItemCategory.Radio.ToString())
            {
                Uri url;
                if (UriParser.Parse(this.SelectedFeedItem.Link, out url))
                {
                    this._player.Play(url);
                }
                
            }
        }

        /// <summary>
        /// Stops player.
        /// </summary>
        public void Stop()
        {
            this._player.Stop();
        }

        /// <summary>
        /// Pauses player.
        /// </summary>
        public void Pause()
        {
            this._player.Pause();
        }        

        /// <summary>
        /// Searches the specified expression.
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <returns></returns>
        public List<FeedItem> Search(string expression)
        {
            List<FeedItem> result = new List<FeedItem>();

            List<FeedItem> res = this.Manager.Search(expression);
            if (res != null)
            {
                result = res;
            }

            return result;
        }

        /// <summary>
        /// Gets the custom list.
        /// </summary>
        /// <returns></returns>
        public List<FeedItem> GetFavoritesList()
        {
            List<FeedItem> result = new List<FeedItem>();

            List<FeedItem> res = this.Manager.GetFavoritesList();
            if (res != null)
            {
                result = res;
            }

            return result;
        }

        /// <summary>
        /// Sets the favorite.
        /// </summary>
        public void ToggleFavorite()
        {
            if (this.SelectedFeedItem != null && this.SelectedFeedItem.Category == FeedItemCategory.Station.ToString())
            {
                this.Manager.ToggleFavorite(this.SelectedFeedItem.Id);
            }
        }

        public List<FeedItem> GetStationList(string genre)
        {
            return this.Manager.GetStationList(genre, string.Empty);
        }

        /// <summary>
        /// Gets the base step.
        /// </summary>
        /// <returns></returns>
        public List<FeedItem> GetBaseStep()
        {
            List<FeedItem> result = new List<FeedItem>();

            if (this.ShoutCastBaseUrl != null)
            {
                List<FeedItem> res = this.Manager.GetGenreList(this.ShoutCastBaseUrl.AbsoluteUri);
                if (res != null)
                {
                    result = res;
                }
            }

            return result;
        }

        /// <summary>
        /// Gets the base step.
        /// </summary>
        /// <returns></returns>
        public List<FeedItem> GetBaseStep(int offsetHours)
        {
            List<FeedItem> result = new List<FeedItem>();

            if (this.ShoutCastBaseUrl != null)
            {
                List<FeedItem> res = this.Manager.GetGenreList(this.ShoutCastBaseUrl.AbsoluteUri, offsetHours);
                if (res != null && res.Count > 0)
                {
                    result = res;
                }
            }

            return result;
        }

        /// <summary>
        /// Gets the next step.
        /// </summary>
        /// <returns></returns>
        public bool GetNextStep(out List<FeedItem> feedList)
        {
            if (this.SelectedFeedItem != null)
            {
                if (this.SelectedFeedItem.Category == FeedItemCategory.Station.ToString())
                {
                    // get pls file, parse it and generate FeedItems
                    List<FeedItem> helperResult = this.Manager.GetStationUrls(this.SelectedFeedItem, this._tmpPlsFilePath);
                    if (helperResult != null && helperResult.Count > 0)
                    {
                        feedList = helperResult;
                        return true;
                    }                    
                }
                else if (this.SelectedFeedItem.Category == FeedItemCategory.Genre.ToString())
                {
                    List<FeedItem> parserResult = this.Manager.GetStationList(this.SelectedFeedItem.Title, this.SelectedFeedItem.Link);

                    if (parserResult != null)
                    {
                        feedList = parserResult;
                        //this._currentGenreItem = this.SelectedFeedItem;
                        return true;
                    }
                }
            }
            feedList = null;
            return false;
        }

        /// <summary>
        /// Gets the previous step.
        /// </summary>
        /// <returns></returns>
        public bool GetPreviousStep(out List<FeedItem> feedList)
        {
            if (this.SelectedFeedItem != null)
            {
                if (this.SelectedFeedItem.Category == FeedItemCategory.Radio.ToString())
                {
                    List<FeedItem> parserResult = this.Manager.GetStationList(this._currentGenreItem.Title, this._currentGenreItem.Link);

                    if (parserResult != null && parserResult.Count > 0)
                    {
                        feedList = parserResult;
                        return true;
                    }
                }
                else if (this.SelectedFeedItem.Category == FeedItemCategory.Station.ToString())
                {
                    feedList = GetBaseStep();
                    return true;
                }
            }
            
            feedList = null;
            return false;
        }

        /// <summary>
        /// Gets the base step asynchronous.
        /// </summary>
        public virtual Guid UpdateAsync()
        {
            // Create an AsyncOperation for taskId.
            Guid taskId = Guid.NewGuid();
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);

            // Multiple threads will access the task dictionary,
            // so it must be locked to serialize access.
            lock (_userStateToLifetime.SyncRoot)
            {
                _userStateToLifetime[taskId] = asyncOp;
            }

            // Start the asynchronous operation.
            UpdateWorkerEventHandler workerDelegate = new UpdateWorkerEventHandler(UpdateWorker);
            workerDelegate.BeginInvoke(this.SelectedFeedItem, asyncOp, null, null);

            return taskId;
        }

        /// <summary>
        /// Gets the base step asynchronous.
        /// </summary>
        public virtual Guid GetBaseStepAsync()
        {
            // Create an AsyncOperation for taskId.
            Guid taskId = Guid.NewGuid();
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);

            // Multiple threads will access the task dictionary,
            // so it must be locked to serialize access.
            lock (_userStateToLifetime.SyncRoot)
            {
                _userStateToLifetime[taskId] = asyncOp;
            }

            // Start the asynchronous operation.
            WorkerEventHandler workerDelegate = new WorkerEventHandler(GetBaseStepWorker);
            workerDelegate.BeginInvoke(this.ShoutCastBaseUrl, string.Empty, FeedItemCategory.Undefined, asyncOp, null, null);

            return taskId;
        }

        /// <summary>
        /// Gets the base step asynchronous.
        /// </summary>
        public virtual Guid GetNextStepAsync()
        {
            // Create an AsyncOperation for taskId.
            Guid taskId = Guid.NewGuid();
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);

            // Multiple threads will access the task dictionary,
            // so it must be locked to serialize access.
            lock (_userStateToLifetime.SyncRoot)
            {
                _userStateToLifetime[taskId] = asyncOp;
            }

            // Start the asynchronous operation.
            WorkerEventHandler workerDelegate = new WorkerEventHandler(GetNextStepWorker);

            Uri baseUrl = null;
            string genre = string.Empty;
            FeedItemCategory currentState = FeedItemCategory.Undefined;
            if (this.SelectedFeedItem != null)
            {
                baseUrl = new Uri(this.SelectedFeedItem.Link);
                currentState = (FeedItemCategory) Enum.Parse(typeof(FeedItemCategory), this.SelectedFeedItem.Category);
                genre = this.SelectedFeedItem.Title;
            }

            workerDelegate.BeginInvoke(baseUrl, genre, currentState, asyncOp, null, null);

            return taskId;
        }

        /// <summary>
        /// Gets the base step asynchronous.
        /// </summary>
        public virtual Guid GetPrevStepAsync()
        {
            // Create an AsyncOperation for taskId.
                Guid taskId = Guid.NewGuid();
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);

            // Multiple threads will access the task dictionary,
            // so it must be locked to serialize access.
            lock (_userStateToLifetime.SyncRoot)
            {
                _userStateToLifetime[taskId] = asyncOp;
            }

            // Start the asynchronous operation.
            WorkerEventHandler workerDelegate = new WorkerEventHandler(GetPrevStepWorker);

            Uri baseUrl = null;
            string genre = string.Empty;
            FeedItemCategory currentState = FeedItemCategory.Undefined;
            if (this.SelectedFeedItem != null && this._currentGenreItem != null)
            {
                baseUrl = new Uri(this._currentGenreItem.Link);                
                currentState = (FeedItemCategory)Enum.Parse(typeof(FeedItemCategory), this.SelectedFeedItem.Category);
                genre = this._currentGenreItem.Genre;
            }            

            workerDelegate.BeginInvoke(baseUrl, genre, currentState, asyncOp, null, null);

            return taskId;
        }

        #endregion

        #region private

        /// <summary>
        /// Gets the next step.
        /// </summary>
        /// <returns></returns>
        private bool GetNextStep(out List<FeedItem> feedList, FeedItem feedItem)
        {
            if (feedItem != null)
            {
                if (feedItem.Category == FeedItemCategory.Station.ToString())
                {
                    // get pls file, parse it and generate FeedItems
                    List<FeedItem> helperResult = this.Manager.GetStationUrls(feedItem, this._tmpPlsFilePath);
                    if (helperResult != null && helperResult.Count > 0)
                    {
                        feedList = helperResult;
                        return true;
                    }
                }
                else if (feedItem.Category == FeedItemCategory.Genre.ToString())
                {
                    List<FeedItem> parserResult = this.Manager.GetStationList(feedItem.Title, feedItem.Link);

                    if (parserResult != null)
                    {
                        feedList = parserResult;
                        //this._currentGenreItem = this.SelectedFeedItem;
                        return true;
                    }
                }
            }
            feedList = null;
            return false;
        }

        /// <summary>
        /// Updates this instance.
        /// </summary>
        /// <returns></returns>
        private List<FeedItem> Update(FeedItem feedItem, AsyncOperation asyncOp)
        {
            List<FeedItem> list = new List<FeedItem>();

            if (feedItem != null)
            {                
                if (feedItem.Category == FeedItemCategory.Genre.ToString())
                {                    
                    ShoutCastManager scm = new ShoutCastManager(this.ShoutCastBaseUrl, this._tmpPlsFilePath, new OnlineBuslogicManager());
                    if (scm.GetNextStep(out list, feedItem))
                    {
                        this.Manager.UpdateStationList(list, feedItem.Title, _tmpPlsFilePath, asyncOp);
                    }
                }
                else if (feedItem.Category == FeedItemCategory.Station.ToString())
                {
                    // get pls file, parse it and generate FeedItems
                    PLSHelper helper = new PLSHelper();
                    string plsString = helper.GetPlsStringFromPlaylist(feedItem.Link, _tmpPlsFilePath);
                    if (!String.IsNullOrEmpty(plsString))
                    {
                        feedItem.Pls = plsString;
                        this.Manager.UpdateStation(feedItem);
                    }
                }
            }

            return new List<FeedItem>(list.OrderBy(a => a.Title));
        }

        /// <summary>
        /// _player_s the on status text changed.
        /// </summary>
        /// <param name="status">The status.</param>
        private void _player_OnStatusTextChanged(string status)
        {
            if (OnPlayerStatusTextChanged != null)
                this.OnPlayerStatusTextChanged(status);
        }

        private void _player_OnMediaChanged()
        {
            if (OnMediaChanged != null)
                this.OnMediaChanged();
        }

        /// <summary>
        /// This method performs the actual GetBaseStep computation.
        /// It is executed on the worker thread.
        /// </summary>
        /// <param name="baseUrl">The base URL.</param>
        /// <param name="currentState">State of the current.</param>
        /// <param name="asyncOp">The async op.</param>
        private void GetNextStepWorker(Uri baseUrl, string genre, FeedItemCategory currentState, AsyncOperation asyncOp)
        {
            Exception e = null;
            List<FeedItem> result = new List<FeedItem>();
            FeedItemCategory state = currentState;
            string currentGenreUrl = string.Empty;

            try
            {
                if (baseUrl != null)
                {
                    if (currentState == FeedItemCategory.Station)
                    {
                        FeedItem item = new FeedItem()
                        {
                            Link = baseUrl.AbsoluteUri
                        };

                        List<FeedItem> helperResult = this.Manager.GetStationUrls(item, this._tmpPlsFilePath);
                        if (helperResult != null && helperResult.Count > 0)
                        {
                            result = helperResult;
                            state = FeedItemCategory.Radio;
                        }
                    }
                    else if (currentState == FeedItemCategory.Genre)
                    {
                        List<FeedItem> parserResult = this.Manager.GetStationList(genre, baseUrl.AbsoluteUri);
                        if (parserResult != null && parserResult.Count > 0)
                        {
                            result = parserResult;
                            state = FeedItemCategory.Station;
                            currentGenreUrl = baseUrl.AbsoluteUri;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                e = ex;
            }

            NextCompletionMethod(result, state, currentGenreUrl, genre, e, false, asyncOp);
        }

        /// <summary>
        /// This method performs the actual GetBaseStep computation.
        /// It is executed on the worker thread.
        /// </summary>
        /// <param name="baseUrl">The base URL.</param>
        /// <param name="currentState">State of the current.</param>
        /// <param name="asyncOp">The async op.</param>
        private void GetPrevStepWorker(Uri baseUrl, string genre, FeedItemCategory currentState, AsyncOperation asyncOp)
        {
            Exception e = null;
            List<FeedItem> result = new List<FeedItem>();
            FeedItemCategory state = currentState;
            string currentGenreUrl = string.Empty;

            try
            {
                if (baseUrl != null)
                {
                    if (currentState == FeedItemCategory.Radio)
                    {
                        List<FeedItem> parserResult = this.Manager.GetStationList(genre, baseUrl.AbsoluteUri);
                        if (parserResult != null && parserResult.Count > 0)
                        {
                            result = parserResult;
                            state = FeedItemCategory.Station;
                        }
                    }
                    else if (currentState == FeedItemCategory.Station)
                    {
                        result = GetBaseStep();
                        if (result != null && result.Count > 0)
                            state = FeedItemCategory.Genre;
                    }
                }
            }
            catch (Exception ex)
            {
                e = ex;
            }

            PrevCompletionMethod(result, state, currentGenreUrl, genre, e, false, asyncOp);
        }

        /// <summary>
        /// Updates the worker.
        /// </summary>
        /// <param name="selectedFeedItem">The selected feed item.</param>
        /// <param name="asyncOp">The async op.</param>
        private void UpdateWorker(FeedItem selectedFeedItem, AsyncOperation asyncOp)
        {
            Exception e = null;
            List<FeedItem> result = new List<FeedItem>();

            try
            {
                result = this.Update(selectedFeedItem, asyncOp);
            }
            catch (Exception ex)
            {
                e = ex;
            }

            UpdateCompletionMethod(result, e, false, asyncOp);
        }

        /// <summary>
        /// This method performs the actual GetBaseStep computation.
        /// It is executed on the worker thread.
        /// </summary>
        /// <param name="baseUrl">The base URL.</param>
        /// <param name="currentState">State of the current.</param>
        /// <param name="asyncOp">The async op.</param>
        private void GetBaseStepWorker(Uri baseUrl, string genre, FeedItemCategory currentState, AsyncOperation asyncOp)
        {
            Exception e = null;
            List<FeedItem> result = new List<FeedItem>();;
            FeedItemCategory state = currentState;

            try
            {
                if (baseUrl != null)
                {
                    result = this.Manager.GetGenreList(baseUrl.AbsoluteUri);
                    state = FeedItemCategory.Genre;

                    if (result == null)
                        throw new Exception("Rss parser returned null");
                }
            }
            catch (Exception ex)
            {
                e = ex;
            }

            BaseCompletionMethod(result, state, string.Empty, genre, e, false, asyncOp);
        }

        private void UpdateProgressMethod(string message, int progress, AsyncOperation asyncOp)
        {
            UpdateProgressEventArgs e = new UpdateProgressEventArgs(message, progress);

            asyncOp.Post(OnUpdateProgressDelegate, e);
        }

        private void UpdateCompletionMethod(List<FeedItem> result, Exception exception, bool canceled, AsyncOperation asyncOp)
        {
            // If the task was not previously canceled,
            // remove the task from the lifetime collection.
            if (!canceled)
            {
                lock (_userStateToLifetime.SyncRoot)
                {
                    _userStateToLifetime.Remove(asyncOp.UserSuppliedState);
                }
            }

            // Package the results of the operation in a 
            // CalculatePrimeCompletedEventArgs.
            UpdateCompletedEventArgs e = new UpdateCompletedEventArgs(result, exception, canceled, asyncOp.UserSuppliedState);

            // End the task. The asyncOp object is responsible 
            // for marshaling the call.
            asyncOp.PostOperationCompleted(onUpdateCompletedDelegate, e);

            // Note that after the call to OperationCompleted, 
            // asyncOp is no longer usable, and any attempt to use it
            // will cause an exception to be thrown.
        }

        /// <summary>
        /// This is the method that the underlying, free-threaded 
        /// asynchronous behavior will invoke.  This will happen on
        /// an arbitrary thread.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="state">The state.</param>
        /// <param name="exception">The exception.</param>
        /// <param name="canceled">if set to <c>true</c> [canceled].</param>
        /// <param name="asyncOp">The async op.</param>
        private void BaseCompletionMethod(List<FeedItem> result, FeedItemCategory state, string currentGenreUrl, string genre, Exception exception, bool canceled, AsyncOperation asyncOp)
        {
            // If the task was not previously canceled,
            // remove the task from the lifetime collection.
            if (!canceled)
            {
                lock (_userStateToLifetime.SyncRoot)
                {
                    _userStateToLifetime.Remove(asyncOp.UserSuppliedState);
                }
            }

            // Package the results of the operation in a 
            // CalculatePrimeCompletedEventArgs.
            GetStepCompletedEventArgs e = new GetStepCompletedEventArgs(result, state, currentGenreUrl, genre, exception, canceled, asyncOp.UserSuppliedState);

            // End the task. The asyncOp object is responsible 
            // for marshaling the call.
            asyncOp.PostOperationCompleted(onBaseStepCompletedDelegate, e);

            // Note that after the call to OperationCompleted, 
            // asyncOp is no longer usable, and any attempt to use it
            // will cause an exception to be thrown.
        }

        /// <summary>
        /// This is the method that the underlying, free-threaded 
        /// asynchronous behavior will invoke.  This will happen on
        /// an arbitrary thread.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="state">The state.</param>
        /// <param name="exception">The exception.</param>
        /// <param name="canceled">if set to <c>true</c> [canceled].</param>
        /// <param name="asyncOp">The async op.</param>
        private void NextCompletionMethod(List<FeedItem> result, FeedItemCategory state, string currentGenreUrl, string genre, Exception exception, bool canceled, AsyncOperation asyncOp)
        {
            // If the task was not previously canceled,
            // remove the task from the lifetime collection.
            if (!canceled)
            {
                lock (_userStateToLifetime.SyncRoot)
                {
                    _userStateToLifetime.Remove(asyncOp.UserSuppliedState);
                }
            }

            // Package the results of the operation in a 
            // CalculatePrimeCompletedEventArgs.
            GetStepCompletedEventArgs e = new GetStepCompletedEventArgs(result, state, currentGenreUrl, genre, exception, canceled, asyncOp.UserSuppliedState);

            // End the task. The asyncOp object is responsible 
            // for marshaling the call.
            asyncOp.PostOperationCompleted(onNextStepCompletedDelegate, e);

            // Note that after the call to OperationCompleted, 
            // asyncOp is no longer usable, and any attempt to use it
            // will cause an exception to be thrown.
        }

        /// <summary>
        /// This is the method that the underlying, free-threaded 
        /// asynchronous behavior will invoke.  This will happen on
        /// an arbitrary thread.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="state">The state.</param>
        /// <param name="exception">The exception.</param>
        /// <param name="canceled">if set to <c>true</c> [canceled].</param>
        /// <param name="asyncOp">The async op.</param>
        private void PrevCompletionMethod(List<FeedItem> result, FeedItemCategory state, string currentGenreUrl, string genre, Exception exception, bool canceled, AsyncOperation asyncOp)
        {
            // If the task was not previously canceled,
            // remove the task from the lifetime collection.
            if (!canceled)
            {
                lock (_userStateToLifetime.SyncRoot)
                {
                    _userStateToLifetime.Remove(asyncOp.UserSuppliedState);
                }
            }

            // Package the results of the operation in a 
            // CalculatePrimeCompletedEventArgs.
            GetStepCompletedEventArgs e = new GetStepCompletedEventArgs(result, state, currentGenreUrl, genre, exception, canceled, asyncOp.UserSuppliedState);

            // End the task. The asyncOp object is responsible 
            // for marshaling the call.
            asyncOp.PostOperationCompleted(onPrevStepCompletedDelegate, e);

            // Note that after the call to OperationCompleted, 
            // asyncOp is no longer usable, and any attempt to use it
            // will cause an exception to be thrown.
        }

        private void UpdateProgressHandler(object operationState)
        {
            UpdateProgressEventArgs e = operationState as UpdateProgressEventArgs;

            OnUpdateProgressChanged(e);
        }

        /// <summary>
        /// Updates the completed.
        /// </summary>
        /// <param name="operationState">State of the operation.</param>
        private void UpdateCompletedHandler(object operationState)
        {
            UpdateCompletedEventArgs e = operationState as UpdateCompletedEventArgs;

            OnUpdateCompleted(e);
        }

        /// <summary>
        /// This method is invoked via the AsyncOperation object,
        /// so it is guaranteed to be executed on the correct thread.
        /// </summary>
        /// <param name="operationState">State of the operation.</param>
        private void BaseStepCompleted(object operationState)
        {
            GetStepCompletedEventArgs e = operationState as GetStepCompletedEventArgs;

            if (!string.IsNullOrEmpty(e.CurrentGenreUrl))
                this._currentGenreItem = new FeedItem()
                {
                    Link = e.CurrentGenreUrl,
                    Title = e.Genre
                };

            OnBaseCompleted(e);
        }

        /// <summary>
        /// This method is invoked via the AsyncOperation object,
        /// so it is guaranteed to be executed on the correct thread.
        /// </summary>
        /// <param name="operationState">State of the operation.</param>
        private void NextStepCompleted(object operationState)
        {
            GetStepCompletedEventArgs e = operationState as GetStepCompletedEventArgs;

            //if (!string.IsNullOrEmpty(e.CurrentGenreUrl))
            //    this._currentGenreItem = new FeedItem()
            //    {
            //        Link = e.CurrentGenreUrl,
            //        Title = e.Genre,
            //        Category = FeedItemCategory.Station.ToString()
            //    };

            OnNextCompleted(e);
        }

        /// <summary>
        /// This method is invoked via the AsyncOperation object,
        /// so it is guaranteed to be executed on the correct thread.
        /// </summary>
        /// <param name="operationState">State of the operation.</param>
        private void PrevStepCompleted(object operationState)
        {
            GetStepCompletedEventArgs e = operationState as GetStepCompletedEventArgs;

            //if (!string.IsNullOrEmpty(e.CurrentGenreUrl))
            //    this._currentGenreItem = new FeedItem()
            //    {
            //        Link = e.CurrentGenreUrl,
            //        Title = e.Genre,
            //        Category = FeedItemCategory.Station.ToString()
            //    };

            OnPrevCompleted(e);
        }

        protected void OnUpdateProgressChanged(UpdateProgressEventArgs e)
        {
            if (UpdateProgressChanged != null)
            {
                UpdateProgressChanged(this, e);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:UpdateCompleted"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Altbrot.Yawr.ShoutCast.GetStepCompletedEventArgs"/> instance containing the event data.</param>
        protected void OnUpdateCompleted(UpdateCompletedEventArgs e)
        {
            if (UpdateCompleted != null)
            {
                UpdateCompleted(this, e);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:CalculatePrimeCompleted"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Altbrot.Yawr.ShoutCast.GetStepCompletedEventArgs"/> instance containing the event data.</param>
        protected void OnBaseCompleted(GetStepCompletedEventArgs e)
        {
            if (GetBaseStepCompleted != null)
            {
                GetBaseStepCompleted(this, e);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:NextCompleted"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Altbrot.Yawr.ShoutCast.GetStepCompletedEventArgs"/> instance containing the event data.</param>
        protected void OnNextCompleted(GetStepCompletedEventArgs e)
        {
            if (GetNextStepCompleted != null)
            {
                GetNextStepCompleted(this, e);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:PrevCompleted"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Altbrot.Yawr.ShoutCast.GetStepCompletedEventArgs"/> instance containing the event data.</param>
        protected void OnPrevCompleted(GetStepCompletedEventArgs e)
        {
            if (GetPrevStepCompleted != null)
            {
                GetPrevStepCompleted(this, e);
            }
        }     

        #endregion
    }
}
